/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */
#pragma once

#include <boost/scoped_ptr.hpp>

#include <dsound.h>
#include <noise/Manager.h>
#include <noise/hard/Channel.h>
#include <math/Vector3.h>
#include <SharedRep.h>
#include <Atom.h>
#include <list>
#include <deque>
#include <MMReg.h>

namespace liba
{
namespace noise
{
namespace hard
{
namespace directx8
{
class Hardware;

class ChannelStaticRepName
{
public:
	Atom channel_type;
	filesystem::String name;
	Hardware * hardware;

	ChannelStaticRepName(Hardware * hardware, const Atom & channel_type, const filesystem::String & name):
		hardware( hardware ),
		channel_type( channel_type ),
		name( name )
	{}
	bool operator<(const ChannelStaticRepName & na)const
	{
		if( hardware < na.hardware )
			return true;
		if( hardware > na.hardware )
			return false;
		if( channel_type < na.channel_type )
			return true;
		if( channel_type > na.channel_type )
			return false;
		return name < na.name;
	}
};

class ChannelStaticRep
{
	WAVEFORMATEXTENSIBLE wfx;
	IDirectSoundBuffer * buffer;
	DWORD size;
	boost::scoped_ptr<DataSource> source;
public:
	ChannelStaticRep(const ChannelStaticRepName & name);
	~ChannelStaticRep();
	IDirectSoundBuffer * create_diplicate(const ChannelStaticRepName & name)const;
	int get_frequency()const
	{
		return wfx.Format.nSamplesPerSec;
	}
	int get_block_align()const
	{
		return wfx.Format.nBlockAlign;
	}
	int get_size()const
	{
		return size;
	}
};

class Channel : public hard::Channel
{
protected:
	properties::DependencyProperty<bool> loop;
	properties::DependencyProperty<double> exact_volume;
	properties::DependencyProperty<double> min_distance;
	properties::DependencyProperty<double> max_distance;
	properties::DependencyProperty<double> exact_pan;
	properties::DependencyProperty< math::Vector3<float> > exact_position;
	properties::DependencyProperty< math::Vector3<float> > exact_speed;
	IDirectSoundBuffer * buffer;
	IDirectSound3DBuffer * buffer3d;

	void SetVolume(double value);
	void SetMinDistance(double value);
	void SetMaxDistance(double value);
	void SetPan(double value);
	void SetPosition(const math::Vector3<float> &value);
	void SetSpeed(const math::Vector3<float> &value);

public:
	Channel();
	~Channel();

private:
	Channel(const Channel &other);
};

class ChannelStatic :
	public Shared<ChannelStaticRepName, ChannelStaticRep>,
	public Channel
{
	DWORD position;
	double approx_vol;
	bool finished;
public:
	static bool SortPredicate(ChannelStatic * a, ChannelStatic * b)
	{
		return a->approx_vol > b->approx_vol;
	}
	ChannelStatic(Hardware * ha, const Atom & channel_type, const filesystem::String & name);
	virtual ~ChannelStatic();
	void life_cycle(double delta_time, const math::Vector3<float> & listener_position);
	bool in_existance()const
	{
		return buffer != 0;
	}
	void come_to_existance();
	void begone();
	virtual void play(DataSource * source);
	virtual void stop();
	virtual bool is_finished();
	void on_hardware_delete();
};

class ChannelStreaming : public Channel
{
public:
	ChannelStreaming(Hardware * hardware, const Atom & channel_type, double length_sec);
	virtual ~ChannelStreaming();

	virtual void play(DataSource * source);
	virtual void stop();
	virtual bool is_finished();

	void life_cycle(double delta_time);
	void on_hardware_delete();
private:
	Hardware * hardware;
	std::deque<DataSource *> sources;
	void destroy_sources();
	void update( DWORD byte_start, DWORD byte_count );

	Atom channel_type;
	WAVEFORMATEXTENSIBLE wfx;
	DWORD size;
	DWORD minimum_update_size;
	DWORD garbage_position;
	DWORD silence_size;
	bool playing;
};


} // namespace directx8
} // namespace hard
} // namespace noise
} // namesapce liba
using namespace liba;

